#include "mainview.h"
#include "insight/insightlenswidget.h"
#include "insight/insightheadsupwidget.h"
#include "scene.h"
#include "pcbpalette.h"
#include "designlayer.h"

#include <QGraphicsItem>
#include <QPainter>
#include <QMouseEvent>
#include <QFrame>
#include <QVBoxLayout>
#include <QLabel>
#include <QDebug>
#include <QToolTip>

namespace FootprintEditor
{

    MainView::MainView(Scene *scene, QWidget *parent) :
        LayoutView(scene, parent)
    {
        m_activeLayer = nullptr;
        m_palette = nullptr;

        m_layerDisplayMode = DisplayAllLayers;

        setMouseTracking(true);

        m_lens = new InsightLensWidget(this);
        m_lens->setBuddyView(this);
        m_lens->setLensShape(InsightLensWidget::SquareLens);
        m_lens->setMouseTracking(true);
        m_lens->setEnabled(true);

        m_headsUp = new InsightHeadsUpWidget(this);
        m_headsUp->move(5, 5);
        enableHeadsUp(true);
    }

    void MainView::addLayer(DesignLayer *layer)
    {
        if (m_indexToLayer.contains(layer->index()))
        {
            return;
        }

        int index = layer->index();
        m_indexToLayer[index] = layer;
        if (layoutScene() != nullptr)
        {
            layoutScene()->addItem(layer);
        }
        if (m_palette)
        {
            layer->setColor(m_palette->color(PcbPalette::ColorRole(layer->index() + 1)));
        }
        emit layerAdded(layer);
    }

    void MainView::removeLayer(DesignLayer *layer)
    {
        if (!m_indexToLayer.contains(layer->index()))
        {
            return;
        }

        int index = layer->index();
        Q_ASSERT(m_indexToLayer[layer->index()] == layer);
        m_indexToLayer.remove(index);
        if (layoutScene() != nullptr)
        {
            layoutScene()->removeItem(layer);
        }
        emit layerRemoved(layer);
    }

    void MainView::addLayers(const QList<DesignLayer *> &layers)
    {
        for (DesignLayer *layer : layers)
        {
            addLayer(layer);
        }
    }

    void MainView::removeLayers(const QList<DesignLayer *> &layers)
    {
        for (DesignLayer *layer : layers)
        {
            removeLayer(layer);
        }
    }

    QList<DesignLayer *> MainView::layers()
    {
        QList<DesignLayer *> list = m_indexToLayer.values();
        qSort(list.begin(), list.end(),
              [](DesignLayer * first, DesignLayer * second)
        {
            return first->index() < second->index();
        });

        return list;
    }

    void MainView::setActiveLayer(DesignLayer *layer)
    {
        Q_ASSERT(m_indexToLayer.values().contains(layer));

        if (layer == m_activeLayer)
        {
            return;
        }

        if (m_activeLayer != nullptr)
        {
            m_activeLayer->setEnabled(false);
        }

        m_activeLayer = layer;
        if (m_activeLayer != nullptr)
        {
            m_activeLayer->setEnabled(true);
        }

        updateLayerDisplayModes();
        updateLayerZValues();

        emit activeLayerChanged(m_activeLayer);
    }

    DesignLayer *MainView::activeLayer()
    {
        return m_activeLayer;
    }

    void MainView::setPcbPalette(PcbPalette *palette)
    {
        if (m_palette == palette)
        {
            return;
        }

        qDebug() << "Switching to palette" << palette->name();

        m_palette = palette;
        Q_ASSERT(palette);

        for (DesignLayer *layer : m_indexToLayer.values())
        {
            layer->setColor(m_palette->color(PcbPalette::ColorRole(layer->index() + 1)));
        }
    }

    PcbPalette *MainView::pcbPalette() const
    {
        return m_palette;
    }

    void MainView::updateLayerDisplayModes()
    {
        for (DesignLayer *layer : layers())
        {
            if (m_activeLayer == layer)
            {
                layer->setColorMode(DesignLayer::NormalColorMode);
                continue;
            }
            switch (m_layerDisplayMode)
            {
                case DisplayAllLayers:
                    layer->setColorMode(DesignLayer::NormalColorMode);
                    break;
                case GreyscaleOtherLayers:
                    layer->setColorMode(DesignLayer::GrayscaledMode);
                    break;
                case MonochromeOtherLayers:
                    layer->setColorMode(DesignLayer::MonochromedMode);
                    break;
                case HideOtherLayers:
                    layer->setColorMode(DesignLayer::HiddenMode);
                    break;
                default:
                    // Not reached
                    Q_ASSERT(false);
            }
        }
        invalidateScene(QRectF(), QGraphicsScene::ItemLayer);
    }

    void MainView::updateLayerZValues()
    {
        int z = 0;
        for (DesignLayer *layer : m_indexToLayer.values())
        {
            if (layer->index() != m_activeLayer->index())
            {
                layer->setZValue(z++);
            }
        }
        if (m_activeLayer != nullptr)
        {
            m_activeLayer->setZValue(z);
        }
    }

    void MainView::addMaskingItem(QGraphicsItem *item)
    {
        if (!m_maskingItems.contains(item))
        {
            m_maskingItems.append(item);
        }
    }

    void MainView::removeMaskingItem(QGraphicsItem *item)
    {
        if (m_maskingItems.contains(item))
        {
            m_maskingItems.removeOne(item);
        }
    }

    void MainView::setMaskingItems(QList<QGraphicsItem *> items)
    {
        m_maskingItems = items;
    }

    QList<QGraphicsItem *> MainView::maskingItems()
    {
        return m_maskingItems;
    }

    void MainView::resetMaskingItems()
    {
        m_maskingItems.clear();
    }

    // TODO: LayerDisplayMode is a layer property, so just update all layers
    void MainView::setLayerDisplayMode(MainView::LayerDisplayMode mode)
    {
        if (m_layerDisplayMode == mode)
        {
            return;
        }

        m_layerDisplayMode = mode;
        updateLayerDisplayModes();
        emit layerDisplayModeChanged(m_layerDisplayMode);
    }

    MainView::LayerDisplayMode MainView::layerDisplayMode() const
    {
        return m_layerDisplayMode;
    }

    MainView::LayerDisplayMode MainView::cycleLayerDisplayMode()
    {
        LayerDisplayMode mode = LayerDisplayMode(int(m_layerDisplayMode) + 1);

        if (mode == _EndDisplayMode)
        {
            mode = _BeginDisplayMode;
        }

        setLayerDisplayMode(mode);
        return layerDisplayMode();
    }

#if 0
    void MainView::setScene(Scene *scene)
    {
        if (m_scene)
        {
            m_scene->disconnect(this);
            m_lens->setBuddyView(nullptr);
            m_connectivity->setBuddyView(nullptr);
            m_headsUp->setBuddyView(nullptr);
            emit sceneRemoved();
        }

        m_scene = scene;

        if (m_scene == nullptr)
        {
            for (DesignLayer *layer : m_indexToLayer.values())
            {
                m_scene->removeItem(layer);
            }
            return;
        }

        QGraphicsView::setScene(scene);
        m_lens->setBuddyView(this);
        m_connectivity->setBuddyView(this);
        m_headsUp->setBuddyView(this);

        for (DesignLayer *layer : m_indexToLayer.values())
        {
            m_scene->addItem(layer);
        }

        emit sceneAdded();
    }

    Scene *MainView::layoutScene() const
    {
        return m_scene;
    }
#endif

    // Should we instead provide access to the design insights object and monitor
    // their property changes?
    bool MainView::headsUpEnabled() const
    {
        return m_headsUp->isEnabled();
    }

    void MainView::enableHeadsUp(bool enabled)
    {
        m_headsUp->setEnabled(enabled);
        m_headsUp->setVisible(enabled);
    }

    bool MainView::headsUpTrackingEnabled() const
    {
        return m_headsUp->displayedItem(InsightHeadsUpWidget::CursorLocation);
    }

    void MainView::enableHeadsUpTracking(bool enabled)
    {
        m_headsUp->setDisplayedItem(InsightHeadsUpWidget::CursorLocation, enabled);
        m_headsUp->setDisplayedItemHover(InsightHeadsUpWidget::CursorLocation, enabled);
    }

    void MainView::resetHeadsUpDeltaOrigin()
    {
        m_headsUp->resetDeltaOrigin();
    }

    bool MainView::headsUpDeltaOriginEnabled() const
    {
        return m_headsUp->displayedItem(InsightHeadsUpWidget::LastClickDelta);
    }

    void MainView::enableHeadsUpDeltaOrigin(bool enabled)
    {
        m_headsUp->setDisplayedItem(InsightHeadsUpWidget::LastClickDelta, enabled);
        m_headsUp->setDisplayedItemHover(InsightHeadsUpWidget::LastClickDelta, enabled);
    }

    bool MainView::insightLensEnabled() const
    {
        return m_lens->isLensEnabled();
    }

    void MainView::enableInsightLens(bool enabled)
    {
        m_lens->setLensEnabled(enabled);
    }

    void MainView::shiftInsightLensToMouse()
    {
        m_lens->moveLensToMousePosition();
    }

    void MainView::enableInsightLensTracking(bool enabled)
    {
        m_lens->setMouseTracking(enabled);
    }

    bool MainView::insightLensMouseTrackingEnabled() const
    {
        return m_lens->mouseTracking();
    }

    bool MainView::insightLensAutoZoomEnabled() const
    {
        // TODO
        return false;
    }

    void MainView::enableInsightLensAutoZoom(bool enabled)
    {
        // TODO
        Q_UNUSED(enabled);
    }

    bool MainView::insightLensSingleLayerEnabled() const
    {
        return false;
    }

    void MainView::enableInsightLensSingleLayerMode(bool enabled)
    {
        // TODO
        Q_UNUSED(enabled);
    }

    void MainView::toggleInsightLensShape()
    {
        m_lens->toggleLensShape();
    }
}
